package tal.shorer.gameoflife;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;

public class GameOfLifeActivity extends Activity {

	private static int[] colors = {
			Color.WHITE,
			Color.RED,
	};
	private static int borderColor = Color.BLACK;
	private static int defaultLen = 10;
	private static String defaultRule = "B3/S23";
	private static long defaultInterval_ms = 1000;

	public int rectDimens;
	public Drawable[] drawables;
	
	private int len;
	private String rule;
	private long interval_ms;
	private int[] digestedBornRule, digestedStayRule;
	private GameOfLifeCell[][] cells;
	private Button stepButton;
	private Button mainButton;
	private boolean mainState;
	private Timer mainTimer;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_game_of_life);

		Intent intent = getIntent();
	    try {
	    	this.len = Integer.parseInt(intent.getStringExtra(MainMenu.EXTRA_MESSAGE_SIZE));
	    }
	    catch (NumberFormatException e) {
	    	Log.w(
	    		this.getString(R.string.app_logging_tag),
	    		String.format("Invalid size. Taking default %d", defaultLen)
    		);
	    	this.len = defaultLen;
	    }
	    if (!this.processRule(intent.getStringExtra(MainMenu.EXTRA_MESSAGE_RULE))) {
	    	Log.w(
	    		this.getString(R.string.app_logging_tag),
	    		String.format("Invalid rule. Taking default %s", defaultRule)
    		);
		    this.rule = defaultRule;
	    	this.processRule(this.rule);
	    }
    	
	    try {
	    	this.interval_ms = Long.parseLong(intent.getStringExtra(MainMenu.EXTRA_MESSAGE_INTERVAL));
	    }
	    catch (NumberFormatException e) {
	    	Log.w(
	    		this.getString(R.string.app_logging_tag),
	    		String.format("Invalid interval. Taking default %d (ms)", defaultInterval_ms)
    		);
	    	this.interval_ms = defaultInterval_ms;
	    }
	    Display display = getWindowManager().getDefaultDisplay();
	    Point point = new Point();
	    display.getSize(point);
	    this.rectDimens = Math.min(point.x, point.y) / this.len ;
	    this.initDrawables();
	    this.initBoard();
	}
	
	private void initDrawables() {
		this.drawables = new Drawable[colors.length];
		for (int i = 0; i < this.drawables.length; i++) {
			Bitmap b = Bitmap.createBitmap(this.rectDimens, this.rectDimens, Bitmap.Config.ARGB_8888);
			for (int x = 0; x < this.rectDimens; x++) {
				for (int y = 0; y < this.rectDimens; y++) {
					boolean isBorder = x == 0 || y == 0 ||
							x == this.rectDimens - 1 ||
							y == this.rectDimens - 1 ; 
					int color = isBorder ? borderColor : colors[i];
					b.setPixel(x, y, color);
				}
			}
			this.drawables[i] = new BitmapDrawable(this.getResources(), b);
		}
	}
	
	private void initBoard() {
	    this.cells = new GameOfLifeCell[this.len][this.len];
	    LinearLayout mainLayout = (LinearLayout)findViewById(R.id.game_of_life_layout);
	    for (int i = 0; i < this.len; i++) {
	    	LinearLayout rowLayout = new LinearLayout(this);
	    	LinearLayout.LayoutParams rowLayoutParams = new LinearLayout.LayoutParams(
	    		LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
	    	rowLayout.setOrientation(LinearLayout.HORIZONTAL);
	    	mainLayout.addView(rowLayout, rowLayoutParams);
	    	for (int j = 0; j < this.len; j++) {
	    		Button btn = new Button(this);
	    		btn.setId(i * this.len + j);
    			rowLayout.addView(btn, this.rectDimens, this.rectDimens);
	    		this.cells[i][j] = new GameOfLifeCell(this, btn, i, j);
			}
	    }
		this.stepButton = new Button(this);
	    this.stepButton.setOnClickListener(new StepButtonOnClickListener(this));
	    this.stepButton.setText(R.string.step_button_text);
	    mainLayout.addView(this.stepButton);
	    this.mainButton = new Button(this);
	    this.mainButton.setOnClickListener(new MainButtonOnClickListener(this));
	    this.mainButton.setText(R.string.main_button_when_stop);
	    mainLayout.addView(this.mainButton);
	    this.mainState = false;
		this.mainTimer = null;
	}
	
	private int[] digestSubRule(String subRule) {
		byte[] bytes = subRule.getBytes();
		int[] result = new int[bytes.length];
		for (int i = 0; i < bytes.length; i++)
			result[i] = bytes[i] - '0';
		Arrays.sort(result);
		return result;
	}
	
	private boolean processRule(String rule) {
		Pattern p = Pattern.compile("[Bb](\\d*)/[Ss](\\d*)");
		Matcher m = p.matcher(rule);
		if (!m.matches())
			return false;
		// No need for try/catch block here because
		// the regex assures only digits are passed
		this.digestedBornRule = this.digestSubRule(m.group(1));
		Log.d(
			this.getString(R.string.app_logging_tag),
			String.format("Digested B rule: %s", Arrays.toString(this.digestedBornRule))
		);
		this.digestedStayRule = this.digestSubRule(m.group(2));
		Log.d(
			this.getString(R.string.app_logging_tag),
			String.format("Digested S rule: %s", Arrays.toString(this.digestedStayRule))
		);
		return true;
	}
	
	private class StepButtonOnClickListener implements OnClickListener {
		
		private GameOfLifeActivity caller;
		
		public StepButtonOnClickListener(GameOfLifeActivity caller) {
			this.caller = caller;
		}
		
		@Override
		public void onClick(View v) {
			this.caller.processStep();
		}
	}
	
	private class MainButtonOnClickListener implements OnClickListener {
		
		private GameOfLifeActivity caller;
		
		public MainButtonOnClickListener(GameOfLifeActivity caller) {
			this.caller = caller;
		}
		
		@Override
		public void onClick(View v) {
			this.caller.mainState = !this.caller.mainState;
			if (this.caller.mainState)
				this.caller.GameOfLifeIsGo();
			else
				this.caller.GameOfLifeIsStop();
		}
	}
	
	private void GameOfLifeIsGo() {
		this.mainButton.setText(R.string.main_button_when_go);
		Log.i(this.getString(R.string.app_logging_tag), "Game of life is go!");
		this.mainTimer = new Timer(true);
		this.reschedule();
	}
	
	private void GameOfLifeIsStop() {
		this.mainButton.setText(R.string.main_button_when_stop);
		Log.i(this.getString(R.string.app_logging_tag), "Game of life is stop!");
		this.mainTimer.cancel();
		this.mainTimer.purge();
		this.mainTimer = null;
	}
	
	public void reschedule() {
		this.mainTimer.schedule(
				new GameOfLifeTimerTask(this),
				this.interval_ms
			);
	}
	
	private int getAdjacent(int i, int j) {
		int adjacent = 0;
		for (int y = i - 1; y <= i + 1; y++)
			for (int x = j - 1; x <= j + 1; x++) {
				if (
					(y == i && x == j) ||
					(y < 0 || y > this.len - 1) ||
					(x < 0 || x > this.len - 1)
					)
					continue;
				if (this.cells[y][x].getState())
					adjacent++;
			}
		return adjacent;
	}

    
    private class SetCellStateRunnable implements Runnable {
    	
    	private GameOfLifeActivity caller;
    	private int i, j;
    	private boolean state;
    	
    	public SetCellStateRunnable (GameOfLifeActivity caller, int i, int j, boolean state) {
    		this.caller = caller;
    		this.i = i;
    		this.j = j;
    		this.state = state;
    	}
    	
    	@Override
        public void run() {
            this.caller.cells[this.i][this.j].setState(this.state);
        }
    }
    
	public void processStep() {
		Log.d(this.getString(R.string.app_logging_tag), "Processing step");
		boolean[][] nextState = new boolean[this.len][this.len];
		int adjacent;
		int[] subRuleToGoBy;
		for (int i = 0; i < this.len; i++) {
			for (int j = 0; j < this.len; j++) {
				adjacent = this.getAdjacent(i, j);
				subRuleToGoBy = this.cells[i][j].getState() ?
					this.digestedStayRule :
					this.digestedBornRule;
				nextState[i][j] = Arrays.binarySearch(subRuleToGoBy, adjacent) > -1;
			}
		}
		for (int i = 0; i < this.len; i++)
			for (int j = 0; j < this.len; j++)
				runOnUiThread(
					new SetCellStateRunnable(
						this, i, j, nextState[i][j]
					)
				);
	}
	
	private class GameOfLifeTimerTask extends TimerTask {
		
		private GameOfLifeActivity caller;
		
		public GameOfLifeTimerTask(GameOfLifeActivity caller) {
			this.caller = caller;
		}
		
		public void run() {
			this.caller.processStep();
			this.caller.reschedule();
		}
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.game_of_life, menu);
		return true;
	}
	   //---create an anonymous class to act as a button click listener---
}
